package store

import (
	"encoding/hex"
	"github.com/gogf/gf/os/gtime"
	"github.com/gogf/gf/util/gconv"
	_ "github.com/mattn/go-sqlite3"
	"gorm.io/gorm"
	ecc2 "sieve_engine/execute/whatsapp/lib/libsignal/ecc"
	groupRecord "sieve_engine/execute/whatsapp/lib/libsignal/groups/state/record"
	identity2 "sieve_engine/execute/whatsapp/lib/libsignal/keys/identity"
	"sieve_engine/execute/whatsapp/lib/libsignal/logger"
	protocol2 "sieve_engine/execute/whatsapp/lib/libsignal/protocol"
	"sieve_engine/execute/whatsapp/lib/libsignal/serialize"
	record2 "sieve_engine/execute/whatsapp/lib/libsignal/state/record"
	"sieve_engine/execute/whatsapp/lib/libsignal/util/bytehelper"
	"sieve_engine/execute/whatsapp/lib/libsignal/util/keyhelper"
	"sieve_engine/execute/whatsapp/lib/protocol/axolotl/empty"
	"sieve_engine/execute/whatsapp/lib/protocol/axolotl/serializer"
	"sieve_engine/execute/whatsapp/lib/protocol/define"
	"sieve_engine/execute/whatsapp/lib/protocol/entity"
	"strconv"
	"strings"
	"sync"
	"unsafe"
)

// Define some in-memory stores for testing.

// IdentityKeyStore
func NewInMemoryIdentityKey(db *gorm.DB, identityKey *identity2.KeyPair, localRegistrationID uint32) *InMemoryIdentityKey {
	i := &InMemoryIdentityKey{
		identityStore:       db,
		trustedKeys:         make(map[*protocol2.SignalAddress]*identity2.Key),
		identityKeyPair:     identityKey,
		localRegistrationID: localRegistrationID,
		trustedKeysLock:     &sync.RWMutex{}, //add by linhx
	}
	return i
}

type Identities struct {
	ID             uint   `gorm:"column:_id;primaryKey;autoIncrement"`
	RecipientId    int64  `gorm:"column:recipient_id;unique"`
	DeviceId       uint32 `gorm:"column:device_id"`
	RegistrationId uint32 `gorm:"column:registration_id"`
	PublicKey      []byte `gorm:"column:public_key"`
	PrivateKey     []byte `gorm:"column:private_key"`
	NextPreKeyId   uint32 `gorm:"column:next_prekey_id"`
	Timestamp      int64  `gorm:"column:timestamp"`
}

type InMemoryIdentityKey struct {
	identityStore       *gorm.DB
	trustedKeys         map[*protocol2.SignalAddress]*identity2.Key
	identityKeyPair     *identity2.KeyPair
	localRegistrationID uint32
	trustedKeysLock     *sync.RWMutex //add by linhx
}

func (i *InMemoryIdentityKey) GetIdentityKeyPair() *identity2.KeyPair {
	if i.identityKeyPair != nil {
		return i.identityKeyPair
	}
	_ = i.queryMyIdentityKeys()
	return i.identityKeyPair
}

func (i *InMemoryIdentityKey) GetLocalRegistrationId() uint32 {
	// 先获取变量值如果有直接返回不查询数据
	if i.localRegistrationID != 0 {
		return i.localRegistrationID
	}

	_ = i.queryMyIdentityKeys()
	return i.localRegistrationID
}

func (i *InMemoryIdentityKey) GetAllIdentityKeys() {
	i.trustedKeysLock.Lock()
	defer i.trustedKeysLock.Unlock()

	if i.identityStore == nil {
		return
	}

	dataArray := make([]*Identities, 0, 0)
	i.identityStore.Find(&dataArray)
	if len(dataArray) > 0 {
		for _, data := range dataArray {
			if data == nil || data.PublicKey == nil {
				continue
			}
			dePubKey, _ := ecc2.DecodePoint(data.PublicKey, 0)
			djbECPublicKey := ecc2.NewDjbECPublicKey(dePubKey.PublicKey())
			trusted := identity2.NewKey(djbECPublicKey)

			intId := *(*int)(unsafe.Pointer(&data.RecipientId))
			nameStr := strconv.Itoa(intId)
			signalAddress := protocol2.NewSignalAddress(nameStr, 0)

			i.trustedKeys[signalAddress] = trusted
		}
	}
	logger.Info("GetAllIdentityKeys:", len(i.trustedKeys))
}
func (i *InMemoryIdentityKey) SaveIdentity(address *protocol2.SignalAddress, identityKey *identity2.Key) {
	i.trustedKeysLock.Lock()
	i.trustedKeys[address] = identityKey
	i.trustedKeysLock.Unlock()

	if i.identityStore != nil {
		var publicKeySerialize []byte

		recipientId := gconv.Int64(address.Name())
		oneRecord := &Identities{
			RecipientId: recipientId,
			DeviceId:    0,
		}

		if identityKey != nil && identityKey.PublicKey() != nil {
			publicKeySerialize = identityKey.PublicKey().Serialize()
			oneRecord.PublicKey = publicKeySerialize
		}

		//_, err := identitiesModel.Replace(insetData)
		//linhx 替换以下代码
		var model Identities
		err := i.identityStore.Model(model).Where("recipient_id=?", recipientId).Updates(oneRecord).Error
		if err != nil {
			logger.Error("SaveIdentity Updates error", err)
			// Create
			err = i.identityStore.Create(oneRecord).Error
			if err != nil {
				logger.Error("SaveIdentity Create error", err)
			}
		}
	}
}

func (i *InMemoryIdentityKey) IsTrustedIdentity(address *protocol2.SignalAddress, identityKey *identity2.Key) bool {
	i.trustedKeysLock.RLock()

	for signalAddress, trusted := range i.trustedKeys {
		if strings.Contains(signalAddress.Name(), address.Name()) {
			ok := trusted == nil || trusted.Fingerprint() == identityKey.Fingerprint()
			i.trustedKeysLock.RUnlock()
			return ok
		}
	}
	i.trustedKeysLock.RUnlock()

	// query data base
	if i.identityStore != nil {
		recipientId := gconv.Int64(address.Name())

		oneRecord := new(Identities)
		err := i.identityStore.Where("recipient_id=?", recipientId).First(oneRecord).Error
		//oneRecord, err := identitiesModel.Where("recipient_id=?", address.Name()).FindOne()
		if err == gorm.ErrRecordNotFound {
			return true
		}
		// public keys
		//if v, ok := oneRecord["public_key"]; ok {
		if oneRecord.PublicKey != nil {
			dePubKey, _ := ecc2.DecodePoint(oneRecord.PublicKey, 0)
			djbECPublicKey := ecc2.NewDjbECPublicKey(dePubKey.PublicKey())
			trusted := identity2.NewKey(djbECPublicKey)
			return trusted == nil || trusted.Fingerprint() == identityKey.Fingerprint()
		}
	}

	return true
}

// queryMyIdentityKeys
func (i *InMemoryIdentityKey) queryMyIdentityKeys() error {
	// query data base
	if i.identityStore != nil {
		oneRecord := new(Identities)
		err := i.identityStore.Where("recipient_id=?", -1).First(oneRecord).Error
		if err == gorm.ErrRecordNotFound {
			return err
		}
		// registration id
		//if v, ok := oneRecord["registration_id"]; ok {
		if oneRecord.RegistrationId > 0 {
			i.localRegistrationID = oneRecord.RegistrationId
		}

		var (
			djbECPublicKey  *ecc2.DjbECPublicKey
			djbECPrivateKey *ecc2.DjbECPrivateKey
		)
		// public keys
		if oneRecord.PublicKey != nil {
			dePubKey, _ := ecc2.DecodePoint(oneRecord.PublicKey, 0)
			djbECPublicKey = ecc2.NewDjbECPublicKey(dePubKey.PublicKey())
		}
		// pri keys
		if oneRecord.PrivateKey != nil {
			djbECPrivateKey = ecc2.NewDjbECPrivateKey(bytehelper.SliceToArray(oneRecord.PrivateKey))
		}
		// set identity keys
		i.identityKeyPair = identity2.NewKeyPair(identity2.NewKey(djbECPublicKey), djbECPrivateKey)
	}
	return nil
}

// saveMyIdentityKeys
func (i *InMemoryIdentityKey) saveMyIdentityKeys(pair *identity2.KeyPair, regId uint32) error {
	if i.identityStore != nil {
		// set inset Data
		insetData := &Identities{
			RecipientId:    -1,
			DeviceId:       0,
			RegistrationId: regId,
			Timestamp:      gtime.TimestampMilli(), //update time
		}

		// public key
		if pair != nil && pair.PublicKey() != nil {
			insetData.PublicKey = pair.PublicKey().PublicKey().Serialize()
		}
		// pri key
		if pair != nil && pair.PrivateKey() != nil {
			insetData.PrivateKey = bytehelper.ArrayToSlice(pair.PrivateKey().Serialize())
		}
		// insert
		err := i.identityStore.Create(insetData).Error
		//_, err := identitiesModel.Insert(insetData)
		if err != nil {
			return err
		}

		// set vars
		i.localRegistrationID = regId
		i.identityKeyPair = pair
	}

	return nil
}

// PreKeyStore
func NewInMemoryPreKey(db *gorm.DB) *InMemoryPreKey {
	return &InMemoryPreKey{
		storesDB:  db,
		store:     make(map[uint32]*record2.PreKey),
		storeLock: &sync.RWMutex{}, //add by linhx
	}
}

type Prekeys struct {
	ID                 uint   `gorm:"column:_id;primaryKey"`
	PrekeyId           uint32 `gorm:"column:prekey_id;unique"`
	SentToServer       bool   `gorm:"column:sent_to_server"`
	Record             []byte `gorm:"column:record"`
	DirectDistribution bool   `gorm:"column:direct_distribution"`
	UploadTimestamp    int64  `gorm:"column:upload_timestamp"`
}

type InMemoryPreKey struct {
	storesDB  *gorm.DB
	store     map[uint32]*record2.PreKey
	storeLock *sync.RWMutex //add by linhx
}

func (i *InMemoryPreKey) LoadPreKey(preKeyID uint32) *record2.PreKey {
	i.storeLock.RLock()
	defer i.storeLock.RUnlock()

	if i.storesDB != nil {
		preKeyResult := new(Prekeys)
		err := i.storesDB.Where("prekey_id=?", preKeyID).First(preKeyResult).Error
		//result, err := preKeysModel.FindOne("prekey_id=?", preKeyID)
		if err == gorm.ErrRecordNotFound {
			return i.store[preKeyID]
		}
		// is  empty
		if empty.IsEmpty(preKeyResult) {
			return i.store[preKeyID]
		}
		// record
		//if recordValue, ok := result["record"]; ok {
		if preKeyResult.Record != nil {
			preKey, err := record2.NewPreKeyFromBytes(preKeyResult.Record, &serializer.ProtoPreKeyRecordSerializer{})
			if err != nil {
				return i.store[preKeyID]
			}
			return preKey
		}
	}
	return i.store[preKeyID]
}

func (i *InMemoryPreKey) LoadAllPreKeys() {
	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	dataArray := make([]*Prekeys, 0, 0)
	i.storesDB.Find(&dataArray)
	if len(dataArray) > 0 {
		for _, preKeyResult := range dataArray {
			if preKeyResult.Record == nil {
				continue
			}
			preKey, err := record2.NewPreKeyFromBytes(preKeyResult.Record, &serializer.ProtoPreKeyRecordSerializer{})
			if err != nil {
				continue
			}
			preKeyID := preKey.ID().Value
			i.store[preKeyID] = preKey
		}
	}
	logger.Info("LoadAllPreKeys:", len(i.store))
}

// 批量保存
func (i *InMemoryPreKey) StorePreKeys(preKeys []*record2.PreKey) {
	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	dataArray := make([]Prekeys, 0)
	for _, preKey := range preKeys {
		preKeyRecord := preKey
		preKeyID := preKey.ID().Value
		i.store[preKeyID] = preKeyRecord

		data := Prekeys{
			PrekeyId:           preKeyID,
			Record:             preKeyRecord.Serialize(),
			SentToServer:       false,
			DirectDistribution: false,
			UploadTimestamp:    gtime.TimestampMilli(),
		}
		dataArray = append(dataArray, data)
	}

	size := len(dataArray)
	if size > 0 && i.storesDB != nil {
		err := i.storesDB.CreateInBatches(&dataArray, size).Error
		//_, err := preKeysModel.CreateInBatches(dataArray, size)
		if err != nil {
			logger.Error("InMemoryPreKey StorePreKeys error", err)
			return
		}
	}
}

func (i *InMemoryPreKey) StorePreKey(preKeyID uint32, preKeyRecord *record2.PreKey) {
	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	i.store[preKeyID] = preKeyRecord
	if i.storesDB != nil {
		data := &Prekeys{
			PrekeyId:           preKeyID,
			Record:             preKeyRecord.Serialize(),
			SentToServer:       false,
			DirectDistribution: false,
			UploadTimestamp:    gtime.TimestampMilli(),
		}
		err := i.storesDB.Create(data).Error
		//_, err := preKeysModel.Insert(data)
		if err != nil {
			logger.Error("InMemoryPreKey StorePreKey error", preKeyID, err)
			return
		}
	}
}

func (i *InMemoryPreKey) ContainsPreKey(preKeyID uint32) bool {
	i.storeLock.RLock()
	defer i.storeLock.RUnlock()

	if i.storesDB != nil {
		var count int64
		i.storesDB.Model(&Prekeys{}).Where("prekey_id=?", preKeyID).Count(&count)
		//isExist, err := preKeysModel.Count("prekey_id=?", preKeyID)
		/*if err != nil {
			logger.Error("InMemoryPreKey ContainsPreKey error", err)
			return false
		}*/
		if count > 0 {
			return true
		}
	}
	_, ok := i.store[preKeyID]
	return ok
}

func (i *InMemoryPreKey) RemovePreKey(preKeyID uint32) {
	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	delete(i.store, preKeyID)
	if i.storesDB != nil {
		deleteValue := new(Prekeys)
		err := i.storesDB.Delete(deleteValue, "prekey_id=?", preKeyID)
		//_, err := preKeysModel.Delete("prekey_id=?", preKeyID)
		if err != nil {
			logger.Error("InMemoryPreKey RemovePreKey error", err)
			return
		}
	}
}

// SessionStore
func NewInMemorySession(serializer *serialize.Serializer, db *gorm.DB) *InMemorySession {
	return &InMemorySession{
		storesDB:     db,
		sessions:     make(map[*protocol2.SignalAddress]*record2.Session),
		serializer:   serializer,
		sessionsLock: &sync.RWMutex{}, //add by linhx
	}
}

type Sessions struct {
	ID          uint   `gorm:"column:_id;primaryKey;autoIncrement"`
	RecipientId int64  `gorm:"column:recipient_id;unique"`
	DeviceId    uint32 `gorm:"column:device_id"`
	Record      []byte `gorm:"column:record"`
	Timestamp   int64  `gorm:"column:timestamp"`
}
type InMemorySession struct {
	storesDB     *gorm.DB
	sessions     map[*protocol2.SignalAddress]*record2.Session
	serializer   *serialize.Serializer
	sessionsLock *sync.RWMutex //add by linhx
}

func (i *InMemorySession) LoadAllSession() {
	dataArray := make([]*Sessions, 0, 0)
	i.storesDB.Find(&dataArray)
	if len(dataArray) > 0 {
		for _, session := range dataArray {
			if session == nil {
				continue
			}
			intId := *(*int)(unsafe.Pointer(&session.RecipientId))
			nameStr := strconv.Itoa(intId)
			signalAddress := protocol2.NewSignalAddress(nameStr, 0)

			protoSessionSerializer := serializer.ProtoSessionSerializer{}
			protoStateSerializer := serializer.ProtoStateSerializer{}
			//logger.Debug("LoadSession:", hex.EncodeToString(session.Record))
			aSession, err := record2.NewSessionFromBytes(session.Record, &protoSessionSerializer, &protoStateSerializer)
			if err != nil {
				continue
			}
			i.sessions[signalAddress] = aSession
		}
	}
	logger.Info("LoadAllSession:", len(i.sessions))
}
func (i *InMemorySession) LoadSession(address *protocol2.SignalAddress) *record2.Session {
	i.sessionsLock.Lock()
	defer i.sessionsLock.Unlock()

	for signalAddress, session := range i.sessions {
		if strings.Contains(signalAddress.Name(), address.Name()) {
			return session
		}
	}

	if i.storesDB != nil {
		session := new(Sessions)
		recipientId := gconv.Int64(address.Name())

		err := i.storesDB.Where("recipient_id=?", recipientId).First(session).Error
		//session, err := sessionsModel.FindOne()
		if err == gorm.ErrRecordNotFound {
			aSession := record2.NewSession(i.serializer.Session, i.serializer.State)
			i.sessions[address] = aSession
			return aSession
		}
		// is empty
		if empty.IsEmpty(session) {
			aSession := record2.NewSession(i.serializer.Session, i.serializer.State)
			i.sessions[address] = aSession
			return aSession
		}
		// get record
		if session.Record == nil {
			//if v, ok := session["record"]; !ok {
			aSession := record2.NewSession(i.serializer.Session, i.serializer.State)
			i.sessions[address] = aSession
			return aSession
		} else {
			protoSessionSerializer := serializer.ProtoSessionSerializer{}
			protoStateSerializer := serializer.ProtoStateSerializer{}
			logger.Debug("LoadSession:", hex.EncodeToString(session.Record))
			aSession, err := record2.NewSessionFromBytes(session.Record, &protoSessionSerializer, &protoStateSerializer)
			if err != nil {
				return nil
			}
			i.sessions[address] = aSession
			return aSession
		}
	}

	session := record2.NewSession(i.serializer.Session, i.serializer.State)
	i.sessions[address] = session
	return session
}

func (i *InMemorySession) GetSubDeviceSessions(name string) []uint32 {
	i.sessionsLock.RLock()
	defer i.sessionsLock.RUnlock()

	var deviceIDs []uint32

	for key := range i.sessions {
		if key.Name() == name && key.DeviceID() != 1 {
			deviceIDs = append(deviceIDs, key.DeviceID())
		}
	}

	return deviceIDs
}
func (i *InMemorySession) StoreSession4Test(phone string) {
	i.sessionsLock.Lock()
	defer i.sessionsLock.Unlock()

	if i.storesDB != nil {
		insertData := &Sessions{
			RecipientId: gconv.Int64(phone),
			Record:      nil,
			DeviceId:    0,
			Timestamp:   gtime.Timestamp(),
		}
		err := i.storesDB.Create(insertData).Error
		if err != nil {
			logger.Error("StoreSession Save error recipient_id = ", phone, insertData.ID)
		}
	}
}
func (i *InMemorySession) StoreSession(remoteAddress *protocol2.SignalAddress, record *record2.Session) {
	i.sessionsLock.Lock()
	defer i.sessionsLock.Unlock()

	i.sessions[remoteAddress] = record
	if i.storesDB != nil {
		var count int64
		//使用 Find、First、Last查询，且传入的参数是结构体类型的时候。就会触发改错误。
		recipientId := gconv.Int64(remoteAddress.Name())
		i.storesDB.Model(&Sessions{}).Where("recipient_id=?", recipientId).Count(&count)
		exist := count > 0
		/*if err != nil {
			logger.Error("StoreSession error recipient_id = ", recipientId, err)
			return
		}*/
		// record bytes
		recordData := record.Serialize()
		//TODO linhx del
		//log.Println("StoreSession recordData:", hex.EncodeToString(recordData))
		// if exist only update if not exist inset
		if exist {
			err := i.storesDB.Model(&Sessions{}).Where("recipient_id=?", recipientId).Update("record", recordData).Error
			if err != nil {
				logger.Error("StoreSession update error recipient_id = ", recipientId, err)
			}
		} else {
			insertData := &Sessions{
				RecipientId: recipientId,
				Record:      recordData,
				DeviceId:    0,
				Timestamp:   gtime.Timestamp(),
			}
			err := i.storesDB.Create(insertData).Error
			if err != nil {
				logger.Error("StoreSession Save error recipient_id = ", recipientId, err)
			}
		}

	}
	//TODO linhx del
	//log.Println("StoreSession", hex.EncodeToString(record.Serialize()))
}

func (i *InMemorySession) ContainsSession(remoteAddress *protocol2.SignalAddress) bool {
	i.sessionsLock.RLock()
	defer i.sessionsLock.RUnlock()

	for signalAddress, _ := range i.sessions {
		if strings.Contains(signalAddress.Name(), remoteAddress.Name()) {
			return true
		}
	}

	if i.storesDB != nil {
		var count int64
		//session := new(Sessions)
		i.storesDB.Model(&Sessions{}).Where("recipient_id=?", gconv.Int64(remoteAddress.Name())).Count(&count)
		//isExist, err := sessionsModel.Count()
		/*if err != nil {
			logger.Error("ContainsSession where error", err)
			return false
		}*/
		// is exist
		if count > 0 {
			return true
		}
	}
	return false
}

func (i *InMemorySession) DeleteSession(remoteAddress *protocol2.SignalAddress) {
	i.sessionsLock.Lock()
	defer i.sessionsLock.Unlock()

	delete(i.sessions, remoteAddress)
}

func (i *InMemorySession) DeleteAllSessions() {
	i.sessionsLock.Lock()
	defer i.sessionsLock.Unlock()

	i.sessions = make(map[*protocol2.SignalAddress]*record2.Session)
}

// SignedPreKeyStore
func NewInMemorySignedPreKey(db *gorm.DB) *InMemorySignedPreKey {
	return &InMemorySignedPreKey{
		signedDatabase: db,
		store:          make(map[uint32]*record2.SignedPreKey),
		storeLock:      &sync.RWMutex{}, //add by linhx
	}
}

type SignedPrekeys struct {
	ID        uint   `gorm:"column:_id;primaryKey;autoIncrement"`
	PreKeyId  uint32 `gorm:"column:prekey_id;unique"`
	Record    []byte `gorm:"column:record"`
	Timestamp int64  `gorm:"column:timestamp"`
}
type InMemorySignedPreKey struct {
	signedDatabase *gorm.DB
	store          map[uint32]*record2.SignedPreKey
	storeLock      *sync.RWMutex //add by linhx
}

func (i *InMemorySignedPreKey) LoadSignedPreKey(signedPreKeyID uint32) *record2.SignedPreKey {
	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	/*if sPreKeys, ok := i.store[signedPreKeyID]; ok {
		return sPreKeys
	}*/
	// query data base
	if i.signedDatabase != nil {
		records := make([]*SignedPrekeys, 0, 0)
		//err := i.signedDatabase.Where("prekey_id=?", signedPreKeyID).First(oneRecord).Error
		err := i.signedDatabase.Where("prekey_id=?", signedPreKeyID).Find(&records).Error
		if len(records) == 0 || err == gorm.ErrRecordNotFound {
			//logger.Error("LoadSignedPreKey error", err)
			return nil
		}
		// record
		//if recordValue, ok := oneRecord["record"]; ok {
		oneRecord := records[0]
		if oneRecord.Record != nil {
			sPreKeys, err := record2.NewSignedPreKeyFromBytes(oneRecord.Record, &serializer.ProtoSignedPreKeyRecordSerializer{})
			if err != nil {
				logger.Error("LoadSignedPreKey error", err)
				return nil
			}
			i.store[signedPreKeyID] = sPreKeys
			return sPreKeys
		}
	}
	return i.store[signedPreKeyID]
}

func (i *InMemorySignedPreKey) LoadAllSignedPreKeys() {
	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	if i.signedDatabase == nil {
		return
	}

	dataArray := make([]*SignedPrekeys, 0, 0)
	i.signedDatabase.Find(&dataArray)
	if len(dataArray) > 0 {
		for _, signedPrekey := range dataArray {
			if signedPrekey == nil {
				continue
			}
			sPreKeys, err := record2.NewSignedPreKeyFromBytes(signedPrekey.Record, &serializer.ProtoSignedPreKeyRecordSerializer{})
			if err != nil {
				continue
			}
			i.store[signedPrekey.PreKeyId] = sPreKeys
		}
	}
	logger.Info("LoadAllSignedPreKeys:", len(i.store))
}

func (i *InMemorySignedPreKey) LoadSignedPreKeys() []*record2.SignedPreKey {
	i.storeLock.RLock()
	defer i.storeLock.RUnlock()

	var preKeys []*record2.SignedPreKey

	for _, record := range i.store {
		preKeys = append(preKeys, record)
	}

	return preKeys
}

func (i *InMemorySignedPreKey) StoreSignedPreKey(signedPreKeyID uint32, record *record2.SignedPreKey) {
	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	i.store[signedPreKeyID] = record
	if i.signedDatabase != nil {
		records := make([]*SignedPrekeys, 0, 0)
		err := i.signedDatabase.Where("prekey_id=?", signedPreKeyID).Find(&records).Error
		//err := i.signedDatabase.Where("prekey_id=?", signedPreKeyID).First(saveData).Error
		if len(records) == 0 || err == gorm.ErrRecordNotFound {
			saveData := new(SignedPrekeys)
			saveData.PreKeyId = signedPreKeyID
			saveData.Timestamp = gtime.TimestampMilli()
			saveData.Record = record.Serialize()

			err = i.signedDatabase.Create(saveData).Error
			if err != nil {
				logger.Error("StoreSignedPreKey error", err)
			}
			return
		}
		saveData := records[0]
		saveData.PreKeyId = signedPreKeyID
		saveData.Timestamp = gtime.TimestampMilli()
		saveData.Record = record.Serialize()

		err = i.signedDatabase.Model(saveData).Where("prekey_id=?", signedPreKeyID).Updates(*saveData).Error
		if err != nil {
			logger.Error("StoreSignedPreKey error", err)
		}
	}
}

func (i *InMemorySignedPreKey) ContainsSignedPreKey(signedPreKeyID uint32) bool {
	i.storeLock.RLock()
	defer i.storeLock.RUnlock()

	_, ok := i.store[signedPreKeyID]
	return ok
}

func (i *InMemorySignedPreKey) RemoveSignedPreKey(signedPreKeyID uint32) {
	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	delete(i.store, signedPreKeyID)
	if i.signedDatabase != nil {
		signedPreKey := new(SignedPrekeys)

		err := i.signedDatabase.Delete(signedPreKey, "prekey_id=?", signedPreKeyID).Error
		if err != nil {
			logger.Error("RemoveSignedPreKey error ", err)
		}
	}
}

// add by linhx
func NewInMemorySendAck(db *gorm.DB) *InMemorySendAck {
	return &InMemorySendAck{
		storesDB:  db,
		store:     make(map[string]*SendAck),
		storeLock: &sync.Mutex{}, //add by linhx
	}
}

type SendAck struct {
	ID          uint             `gorm:"column:_id;primaryKey;autoIncrement"`
	MsgId       string           `gorm:"column:msg_id;not null"`
	MsgType     define.MsgStatus `gorm:"column:msg_type;not null"`
	RecipientId string           `gorm:"column:recipient_id"`
}

type InMemorySendAck struct {
	storesDB  *gorm.DB
	store     map[string]*SendAck
	storeLock *sync.Mutex
}

func (i *InMemorySendAck) StoreSendAck(e *entity.Receipt) {
	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	var msgStatus define.MsgStatus
	if e.ReceiptType == "" {
		msgStatus = define.Ack
	} else if strings.Contains(e.ReceiptType, "read") {
		msgStatus = define.Read
	} else {
		logger.Error("StoreSendAck MsgStatus no handler ", e)
		return
	}

	u := e.RecipientId
	if strings.Contains(u, "@") {
		u = strings.Split(u, "@")[0]
	}

	sendAck := &SendAck{
		MsgId:       e.MsgId,
		MsgType:     msgStatus,
		RecipientId: u,
	}
	i.store[sendAck.MsgId] = sendAck
	if i.storesDB != nil {
		i.storesDB.Create(sendAck)
	}
}

func (i *InMemorySendAck) LoadStoreSendAck() []*SendAck {
	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	dataArray := make([]*SendAck, 0)
	for _, data := range i.store {
		if data == nil {
			continue
		}
		dataArray = append(dataArray, data)
	}
	return dataArray
}

func (i *InMemorySendAck) loadAllSendAck() []*SendAck {
	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	dataArray := make([]*SendAck, 0, 0)

	if i.storesDB == nil {
		return dataArray
	}

	i.storesDB.Find(&dataArray)

	for _, data := range dataArray {
		if data == nil {
			continue
		}
		i.store[data.MsgId] = data
	}
	logger.Info("LoadAllSendAck:", len(i.store))
	return dataArray
}

/*func (i *InMemorySendAck) LoadSendAck(recipientId string) *SendAck {
	if recipientId == "" {
		return nil
	}

	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	if i.storesDB != nil {
		sendAck := new(SendAck)
		err := i.storesDB.Where("recipient_id=?", recipientId).First(sendAck).Error
		if err == gorm.ErrRecordNotFound {
			return nil
		}
		return sendAck
	}
	return nil
}*/

func (i *InMemorySendAck) DelSendAck(msgId string) *SendAck {
	if msgId == "" {
		return nil
	}

	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	delete(i.store, msgId)
	if i.storesDB != nil {
		sendAck := new(SendAck)
		i.storesDB.Delete(sendAck, "msg_id=?", msgId)
		return sendAck
	}
	return nil
}

//add by linhx end

func NewInMemorySenderKey(db *gorm.DB) *InMemorySenderKey {
	return &InMemorySenderKey{
		storesDB:  db,
		store:     make(map[*protocol2.SenderKeyName]*groupRecord.SenderKey),
		storeLock: &sync.RWMutex{}, //add by linhx
	}
}

type SenderKeys struct {
	ID       uint   `gorm:"column:_id;primaryKey;autoIncrement"`
	GroupId  string `gorm:"column:group_id;not null"`
	SenderId int64  `gorm:"column:sender_id;not null"`
	Record   []byte `gorm:"column:record;not null"`
}

type InMemorySenderKey struct {
	storesDB  *gorm.DB
	store     map[*protocol2.SenderKeyName]*groupRecord.SenderKey
	storeLock *sync.RWMutex //add by linhx
}

func (i *InMemorySenderKey) StoreSenderKey(senderKeyName *protocol2.SenderKeyName, keyRecord *groupRecord.SenderKey) {
	//i.storeLock.Lock()
	//defer i.storeLock.Unlock()
	//i.store[senderKeyName] = keyRecord
	if i.storesDB != nil {
		senderKey := new(SenderKeys)
		senderId := gconv.Int64(senderKeyName.Sender().Name())
		recordData := keyRecord.Serialize()

		err := i.storesDB.Where("group_id=? AND sender_id=?", senderKeyName.GroupID(), senderId).First(senderKey).Error
		//err := i.storesDB.Where("group_id=? AND sender_id=?", senderKeyName.GroupID(), senderId).First(senderKey).Error
		// is Exist
		if err == gorm.ErrRecordNotFound {
			senderKey.GroupId = senderKeyName.GroupID()
			senderKey.SenderId = senderId
			senderKey.Record = recordData

			err = i.storesDB.Create(senderKey).Error
			if err != nil {
				logger.Error("StoreSenderKey Save error group_id = ", senderKeyName.GroupID(), err)
			}
			return
		}

		// update
		err = i.storesDB.Model(&SenderKeys{}).Where("group_id=? AND sender_id=?", senderKeyName.GroupID(), senderId).Update("record", recordData).Error
		if err != nil {
			logger.Error("StoreSenderKey update error group_id = ", senderKeyName.GroupID(), err)
		}
	}
}

func (i *InMemorySenderKey) LoadAllSenderKey() {
	i.storeLock.Lock()
	defer i.storeLock.Unlock()

	if i.storesDB == nil {
		return
	}

	dataArray := make([]*SenderKeys, 0, 0)
	i.storesDB.Find(&dataArray)
	if len(dataArray) > 0 {
		for _, data := range dataArray {
			if data == nil || data.Record == nil {
				continue
			}

			senderKeySessionSerializer := &serializer.ProtoSenderKeySessionSerializer{}
			sendKeyStateSerializer := &serializer.ProtoSenderKeyStateSerializer{}
			senderKey, err := groupRecord.NewSenderKeyFromBytes(data.Record, senderKeySessionSerializer, sendKeyStateSerializer)
			if err != nil {
				continue
			}

			intId := *(*int)(unsafe.Pointer(&data.SenderId))
			nameStr := strconv.Itoa(intId)
			signalAddress := protocol2.NewSignalAddress(nameStr, 0)
			senderKeyName := protocol2.NewSenderKeyName(data.GroupId, signalAddress)
			i.store[senderKeyName] = senderKey
		}
	}
	logger.Info("LoadAllSenderKey:", len(i.store))
}
func (i *InMemorySenderKey) LoadSenderKey(senderKeyName *protocol2.SenderKeyName) *groupRecord.SenderKey {
	//i.storeLock.Lock()
	//defer i.storeLock.Unlock()

	// 先在内存中查找
	/*for v, key := range i.store {
		if strings.Contains(v.GroupID(), senderKeyName.GroupID()) &&
			strings.Contains(v.Sender().Name(), senderKeyName.Sender().Name()) {
			return key
		}
	}*/
	// id
	groupId := senderKeyName.GroupID()
	senderId := gconv.Int64(senderKeyName.Sender().Name())
	// stores database
	if i.storesDB != nil {
		oneData := new(SenderKeys)
		err := i.storesDB.Where("group_id=? AND sender_id=?", groupId, senderId).First(oneData).Error
		//oneData, err := senderKeysModel.FindOne()
		if err == gorm.ErrRecordNotFound {
			//logger.Error("LoadSenderKey findOne error ", groupId, senderId, err)
			return nil
		}
		// is Empty
		if empty.IsEmpty(oneData) {
			return nil
		}
		// recordValue
		if oneData.Record != nil {
			senderKeySessionSerializer := &serializer.ProtoSenderKeySessionSerializer{}
			sendKeyStateSerializer := &serializer.ProtoSenderKeyStateSerializer{}
			senderKey, err := groupRecord.NewSenderKeyFromBytes(oneData.Record, senderKeySessionSerializer, sendKeyStateSerializer)
			if err != nil {
				return nil
			}
			//i.store[senderKeyName] = senderKey
			return senderKey
		}
	}
	return nil
}

// SignalStore
type SignalStore struct {
	storeDataBase     *gorm.DB
	SessionStore      *InMemorySession
	PreKeyStore       *InMemoryPreKey
	SignedPreKeyStore *InMemorySignedPreKey
	IdentityStore     *InMemoryIdentityKey
	SenderKeyStore    *InMemorySenderKey
	SendAckStore      *InMemorySendAck

	Serialize *serialize.Serializer
}

func (s *SignalStore) initSignalStore(msgPubKey, msgPriKey []byte, rid string) error {
	var (
		err             error
		signedPreKey    *record2.SignedPreKey
		identityKeyPair *identity2.KeyPair
		registrationID  uint32
	)
	// get serializer
	signedPreKeyRecordSerializer := &serializer.ProtoSignedPreKeyRecordSerializer{}
	// Generate an identity keypair
	//identityKeyPair, err = keyhelper.GenerateIdentityKeyPair()
	if msgPubKey == nil {
		identityKeyPair, err = keyhelper.GenerateIdentityKeyPair()
	} else {
		var msgPubKeyByte32 [32]byte
		copy(msgPubKeyByte32[:], msgPubKey)

		var msgPriKeyByte32 [32]byte
		copy(msgPriKeyByte32[:], msgPriKey)

		identityKeyPair, err = keyhelper.GenerateIdentityKeyPairByBytes(msgPubKeyByte32, msgPriKeyByte32)
	}
	if err != nil {
		return nil
	}
	// Generate an  registration id
	//registrationID = keyhelper.GenerateRegistrationID()
	if rid != "" {
		ridCode, err1 := strconv.Atoi(rid)
		if err1 != nil {
			registrationID = keyhelper.GenerateRegistrationID()
		} else {
			registrationID = uint32(ridCode)
		}
	} else {
		registrationID = keyhelper.GenerateRegistrationID()
	}

	//  Generate Signed Prekeys
	signedPreKey, err = keyhelper.GenerateSignedPreKey(identityKeyPair, 0, signedPreKeyRecordSerializer)
	if err != nil {
		return nil
	}
	// set registration id and identityKeys to identity storeDataBase
	if err := s.IdentityStore.saveMyIdentityKeys(identityKeyPair, registrationID); err != nil {
		return err
	}
	// save signed pre keys
	s.SignedPreKeyStore.StoreSignedPreKey(
		signedPreKey.ID(),
		record2.NewSignedPreKey(
			signedPreKey.ID(),
			signedPreKey.Timestamp(),
			signedPreKey.KeyPair(),
			signedPreKey.Signature(),
			signedPreKeyRecordSerializer,
		),
	)
	return nil
}

func (s *SignalStore) ReStore() {
	s.SessionStore.LoadAllSession()
	s.PreKeyStore.LoadAllPreKeys()
	s.SignedPreKeyStore.LoadAllSignedPreKeys()
	s.IdentityStore.GetAllIdentityKeys()
	s.SenderKeyStore.LoadAllSenderKey()
	s.SendAckStore.loadAllSendAck()
}

// NewSignalStore
func NewSignalStore(db *gorm.DB, needInit bool, msgPubKey, msgPriKey []byte, rid string) *SignalStore {
	// serialize
	newProtoSerializer := serializer.NewProtoSerializer()
	// create instance
	s := &SignalStore{
		storeDataBase:     db,
		SessionStore:      NewInMemorySession(newProtoSerializer, db),
		PreKeyStore:       NewInMemoryPreKey(db),
		SignedPreKeyStore: NewInMemorySignedPreKey(db),
		IdentityStore:     NewInMemoryIdentityKey(db, nil, 0),
		SenderKeyStore:    NewInMemorySenderKey(db),
		SendAckStore:      NewInMemorySendAck(db),

		Serialize: newProtoSerializer,
	}
	// need init
	if needInit {
		_ = s.initSignalStore(msgPubKey, msgPriKey, rid)
	}

	// Generate a registration id
	//
	//priKeyData, _ := hex.DecodeString("70d39c86f3e17e3ac93da9a53541dcdda5647bf618463499b48b0d61e5aa2c7a")
	//djbECPrivateKey := ecc.NewDjbECPrivateKey(bytehelper.SliceToArray(priKeyData))
	//
	//pubKeyData, _ := hex.DecodeString("053bbb934014e04dd100a874e5a328b536788d4fe7aba4f092b301d6584e2ac81e")
	//dePubKey, _ := ecc.DecodePoint(pubKeyData, 0)
	//djbECPublicKey := ecc.NewDjbECPublicKey(dePubKey.PublicKey())
	//log.Println(pubKeyData)
	//
	//newKeyPair := identity.NewKeyPair(identity.NewKey(djbECPublicKey), djbECPrivateKey)
	return s
}
